// Copyright 2016 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com

#ifndef CORE_FPDFAPI_PARSER_CPDF_DATA_AVAIL_H_
#define CORE_FPDFAPI_PARSER_CPDF_DATA_AVAIL_H_

#include <functional>
#include <map>
#include <memory>
#include <set>
#include <utility>
#include <vector>

#include "core/fpdfapi/parser/cpdf_document.h"
#include "core/fpdfapi/parser/cpdf_parser.h"
#include "core/fxcrt/retain_ptr.h"
#include "core/fxcrt/unowned_ptr.h"

class CPDF_CrossRefAvail;
class CPDF_Dictionary;
class CPDF_HintTables;
class CPDF_IndirectObjectHolder;
class CPDF_LinearizedHeader;
class CPDF_PageObjectAvail;
class CPDF_ReadValidator;
class CPDF_SyntaxParser;

class CPDF_DataAvail final : public Observable::ObserverIface {
 public:
  // Must match PDF_DATA_* definitions in public/fpdf_dataavail.h, but cannot
  // #include that header. fpdfsdk/fpdf_dataavail.cpp has static_asserts
  // to make sure the two sets of values match.
  enum DocAvailStatus {
    kDataError = -1,        // PDF_DATA_ERROR
    kDataNotAvailable = 0,  // PDF_DATA_NOTAVAIL
    kDataAvailable = 1,     // PDF_DATA_AVAIL
  };

  // Must match PDF_*LINEAR* definitions in public/fpdf_dataavail.h, but cannot
  // #include that header. fpdfsdk/fpdf_dataavail.cpp has static_asserts
  // to make sure the two sets of values match.
  enum DocLinearizationStatus {
    kLinearizationUnknown = -1,  // PDF_LINEARIZATION_UNKNOWN
    kNotLinearized = 0,          // PDF_NOT_LINEARIZED
    kLinearized = 1,             // PDF_LINEARIZED
  };

  // Must match PDF_FORM_* definitions in public/fpdf_dataavail.h, but cannot
  // #include that header. fpdfsdk/fpdf_dataavail.cpp has static_asserts
  // to make sure the two sets of values match.
  enum DocFormStatus {
    kFormError = -1,        // PDF_FORM_ERROR
    kFormNotAvailable = 0,  // PDF_FORM_NOTAVAIL
    kFormAvailable = 1,     // PDF_FORM_AVAIL
    kFormNotExist = 2,      // PDF_FORM_NOTEXIST
  };

  class FileAvail {
   public:
    virtual ~FileAvail();
    virtual bool IsDataAvail(FX_FILESIZE offset, size_t size) = 0;
  };

  class DownloadHints {
   public:
    virtual ~DownloadHints();
    virtual void AddSegment(FX_FILESIZE offset, size_t size) = 0;
  };

  CPDF_DataAvail(FileAvail* pFileAvail,
                 RetainPtr<IFX_SeekableReadStream> pFileRead);
  ~CPDF_DataAvail() override;

  // Observable::ObserverIface:
  void OnObservableDestroyed() override;

  DocAvailStatus IsDocAvail(DownloadHints* pHints);
  DocAvailStatus IsPageAvail(uint32_t dwPage, DownloadHints* pHints);
  DocFormStatus IsFormAvail(DownloadHints* pHints);
  DocLinearizationStatus IsLinearizedPDF();
  int GetPageCount() const;
  RetainPtr<const CPDF_Dictionary> GetPageDictionary(int index) const;
  RetainPtr<CPDF_ReadValidator> GetValidator() const;

  std::pair<CPDF_Parser::Error, std::unique_ptr<CPDF_Document>> ParseDocument(
      std::unique_ptr<CPDF_Document::RenderDataIface> pRenderData,
      std::unique_ptr<CPDF_Document::PageDataIface> pPageData,
      const ByteString& password);

  const CPDF_HintTables* GetHintTablesForTest() const {
    return m_pHintTables.get();
  }

 private:
  enum class InternalStatus : uint8_t {
    kHeader = 0,
    kFirstPage,
    kHintTable,
    kLoadAllCrossRef,
    kRoot,
    kInfo,
    kPageTree,
    kPage,
    kPageLaterLoad,
    kResources,
    kDone,
    kError,
    kLoadAllFile,
  };

  class PageNode {
   public:
    enum class Type { kUnknown = 0, kPage, kPages, kArray };

    PageNode();
    ~PageNode();

    Type m_type = Type::kUnknown;
    uint32_t m_dwPageNo = 0;
    std::vector<std::unique_ptr<PageNode>> m_ChildNodes;
  };

  static constexpr int kMaxPageRecursionDepth = 1024;

  bool CheckDocStatus();
  bool CheckHeader();
  bool CheckFirstPage();
  bool CheckHintTables();
  bool CheckRoot();
  bool CheckInfo();
  bool CheckPages();
  bool CheckPage();
  DocAvailStatus CheckResources(RetainPtr<CPDF_Dictionary> page);
  DocFormStatus CheckAcroForm();
  bool CheckPageStatus();

  DocAvailStatus CheckHeaderAndLinearized();
  RetainPtr<CPDF_Object> ParseIndirectObjectAt(
      FX_FILESIZE pos,
      uint32_t objnum,
      CPDF_IndirectObjectHolder* pObjList) const;
  RetainPtr<CPDF_Object> GetObject(uint32_t objnum, bool* pExistInFile);
  bool GetPageKids(CPDF_Object* pPages);
  bool PreparePageItem();
  bool LoadPages();
  bool CheckAndLoadAllXref();
  bool LoadAllFile();
  DocAvailStatus CheckLinearizedData();

  bool CheckPage(uint32_t dwPage);
  bool LoadDocPages();
  bool LoadDocPage(uint32_t dwPage);
  bool CheckPageNode(const PageNode& pageNode,
                     int32_t iPage,
                     int32_t& iCount,
                     int level);
  bool CheckUnknownPageNode(uint32_t dwPageNo, PageNode* pPageNode);
  bool CheckArrayPageNode(uint32_t dwPageNo, PageNode* pPageNode);
  bool CheckPageCount();
  bool IsFirstCheck(uint32_t dwPage);
  void ResetFirstCheck(uint32_t dwPage);
  bool ValidatePage(uint32_t dwPage) const;
  CPDF_SyntaxParser* GetSyntaxParser() const;

  RetainPtr<CPDF_ReadValidator> m_pFileRead;
  CPDF_Parser m_parser;
  RetainPtr<CPDF_Dictionary> m_pRoot;
  std::unique_ptr<CPDF_LinearizedHeader> m_pLinearized;
  bool m_bDocAvail = false;
  InternalStatus m_internalStatus = InternalStatus::kHeader;
  std::unique_ptr<CPDF_CrossRefAvail> m_pCrossRefAvail;
  const FX_FILESIZE m_dwFileLen;
  UnownedPtr<CPDF_Document> m_pDocument;
  std::vector<uint32_t> m_PageObjList;
  std::set<uint32_t> m_SeenPageObjList;
  uint32_t m_PagesObjNum = 0;
  bool m_bLinearedDataOK = false;
  bool m_bMainXRefLoadTried = false;
  bool m_bMainXRefLoadedOK = false;
  bool m_bPagesTreeLoad = false;
  bool m_bPagesLoad = false;
  std::unique_ptr<CPDF_PageObjectAvail> m_pFormAvail;
  std::vector<RetainPtr<CPDF_Object>> m_PagesArray;
  bool m_bTotalLoadPageTree = false;
  bool m_bCurPageDictLoadOK = false;
  bool m_bHeaderAvail = false;
  PageNode m_PageNode;
  std::set<uint32_t> m_pageMapCheckState;
  std::set<uint32_t> m_pagesLoadState;
  std::unique_ptr<CPDF_HintTables> m_pHintTables;
  std::map<uint32_t, std::unique_ptr<CPDF_PageObjectAvail>> m_PagesObjAvail;
  std::map<RetainPtr<const CPDF_Object>,
           std::unique_ptr<CPDF_PageObjectAvail>,
           std::less<>>
      m_PagesResourcesAvail;
};

#endif  // CORE_FPDFAPI_PARSER_CPDF_DATA_AVAIL_H_
 

以下是 `CPDF_DataAvail` 类中提供的接口功能：

### 枚举类型
1. **DocAvailStatus**: 指示文档可用性状态的枚举。包括：
   - `kDataError`: 数据错误。
   - `kDataNotAvailable`: 数据不可用。
   - `kDataAvailable`: 数据可用。

2. **DocLinearizationStatus**: 指示文档线性化状态的枚举。包括：
   - `kLinearizationUnknown`: 线性化状态未知。
   - `kNotLinearized`: 文档未线性化。
   - `kLinearized`: 文档已线性化。

3. **DocFormStatus**: 指示文档表单状态的枚举。包括：
   - `kFormError`: 表单错误。
   - `kFormNotAvailable`: 表单不可用。
   - `kFormAvailable`: 表单可用。
   - `kFormNotExist`: 表单不存在。

### 嵌套类
4. **class FileAvail**: 表示文件可用性检查的接口类。
   - **IsDataAvail(FX_FILESIZE offset, size_t size)**: 检查从 `offset` 开始的 `size` 数据是否可用。

5. **class DownloadHints**: 表示下载提示的接口类。
   - **AddSegment(FX_FILESIZE offset, size_t size)**: 添加需要下载的数据段。

### 构造函数与析构函数
6. **CPDF_DataAvail(FileAvail* pFileAvail, RetainPtr<IFX_SeekableReadStream> pFileRead)**: 构造函数，初始化文件可用性检查和文件读取流。
7. **~CPDF_DataAvail()**: 析构函数。

### 公共方法
8. **void OnObservableDestroyed() override**: 当观察到的对象被销毁时调用。

9. **DocAvailStatus IsDocAvail(DownloadHints* pHints)**: 检查整个文档是否可用。
10. **DocAvailStatus IsPageAvail(uint32_t dwPage, DownloadHints* pHints)**: 检查指定页面是否可用。
11. **DocFormStatus IsFormAvail(DownloadHints* pHints)**: 检查文档表单是否可用。
12. **DocLinearizationStatus IsLinearizedPDF()**: 检查文档是否线性化。
13. **int GetPageCount() const**: 获取页面数量。
14. **RetainPtr<const CPDF_Dictionary> GetPageDictionary(int index) const**: 获取指定页索引的页字典。
15. **RetainPtr<CPDF_ReadValidator> GetValidator() const**: 获取读取验证器。

16. **std::pair<CPDF_Parser::Error, std::unique_ptr<CPDF_Document>> ParseDocument(...)**: 解析PDF文档。

### 私有方法
17. **bool CheckDocStatus()**: 检查文档状态。
18. **bool CheckHeader()**: 检查文档头。
19. **bool CheckFirstPage()**: 检查第一页。
20. **bool CheckHintTables()**: 检查提示表。
21. **bool CheckRoot()**: 检查文档根。
22. **bool CheckInfo()**: 检查文档信息。
23. **bool CheckPages()**: 检查页面。
24. **bool CheckPage()**: 检查当前页面。
25. **DocAvailStatus CheckResources(RetainPtr<CPDF_Dictionary> page)**: 检查页面资源。
26. **DocFormStatus CheckAcroForm()**: 检查AcroForm。
27. **bool PreparePageItem()**: 准备页面项。
28. **bool LoadPages()**: 加载页面。
29. **bool CheckAndLoadAllXref()**: 检查并加载所有交叉引用。
30. **bool LoadAllFile()**: 加载整个文件。
31. **RetainPtr<CPDF_Object> ParseIndirectObjectAt(...)**: 解析指定位置的间接对象。
32. **RetainPtr<CPDF_Object> GetObject(uint32_t objnum, bool* pExistInFile)**: 获取指定编号的对象。

这些接口用于提供对 PDF 文档数据可用性的检查和解析功能，支持增量加载和流式读取。

*/